#pragma once
#include <string>
#include <vector>
#include <bitset>
#include <cctype>
#include <algorithm>
#include <jsoncpp/json/json.h>
#include "../index/index.hpp"
#include "../util/util.hpp"
namespace boost_searcher
{
    struct inverted_list_print_t
    {
        inverted_list_print_t() : doc_id(0), weight(0) { ; }
        uint64_t doc_id;
        int weight;
        std::vector<std::string> key_words;
    };
    class searcher_t
    {
    public:
        searcher_t() { ; };
        ~searcher_t() { ; };

    public:
        void init_searcher(const std::string &file_path)
        {
            // 1.获取index单例
            _index = bs_index::index_t::get_instance();
            boost_searcher::searcher_log(NORMAL, "building index...");
            // 2.使用index对象创建索引
            _index->build_index(file_path);
        }
        void search(const std::string &search_words, std::string &return_string)
        {
            // 1.分词 对search_words进行分词
            std::vector<std::string> cut_result;
            bs_util::jieba_util::cut_string(search_words, cut_result);
            // 2.触发 得到倒排拉链,与构建索引一样忽略大小写
            // 倒排元素集合
            std::vector<inverted_list_print_t> inverted_list_print_all;
            //  哈希表过滤去重
            std::unordered_map<uint64_t, inverted_list_print_t> tokens_map;
            // 3.合并 合并拉链 去重并按照相关性(weight)排序
            for (auto word : cut_result)
            {
                // 转换为小写
                boost::to_lower(word);
                // 获取每个词映射的拉链
                bs_index::inverted_list_t *inverted_list = _index->get_inverted_list(word);
                if (nullptr == inverted_list)
                {
                    continue;
                }
                // 集合
                for (auto e : *inverted_list)
                {
                    // 去重
                    auto &map_elem = tokens_map[e.doc_id];
                    map_elem.doc_id = e.doc_id;
                    map_elem.key_words.push_back(e.key_word);
                    map_elem.weight += e.weight;
                }
                // inverted_list_all.insert(inverted_list_all.end(), inverted_list->begin(), inverted_list->end());
            }
            for (auto &e : tokens_map)
            {
                // 汇总
                inverted_list_print_all.push_back(std::move(e.second));
            }
            //  利用sort按照相关性(weight)排序
            std::sort(inverted_list_print_all.begin(), inverted_list_print_all.end(),
                      [](const inverted_list_print_t &elem_a, const inverted_list_print_t &elem_b)
                          -> bool
                      { return elem_a.weight > elem_b.weight; });
            // 4.构建 构建返回json串
            Json::Value root;
            for (auto e : inverted_list_print_all)
            {
                // 查询正排索引
                bs_index::doc_info_t *p_doc_info = _index->get_doc_info(e.doc_id);
                if (p_doc_info == nullptr)
                {
                    continue;
                }
                // 提取信息并序列化
                Json::Value item;
                item["title"] = p_doc_info->title;
                item["desc"] = get_desc(p_doc_info->content, e.key_words[0]); // 提取摘要
                item["url"] = p_doc_info->url;
                item["id"] = std::to_string(e.doc_id);
                std::string word_str;
                for (auto &word : e.key_words)
                {
                    word_str += std::move(word);
                }
                item["word"] = std::move(word_str);
                item["weight"] = e.weight;
                root.append(item);
            }
            Json::FastWriter fast_writer;
            return_string = fast_writer.write(root);
        }

    private:
        std::string get_desc(std::string content, std::string word)
        {
            auto iter = std::search(content.begin(), content.end(), word.begin(), word.end(), [](const char a, const char b) -> bool
                                    { return std::tolower(a) == std::tolower(b); });
            if (iter == content.end())
            {
                boost_searcher::searcher_log(WARNING,"failed to find word in content!");
                return std::move(std::string("null"));
            }
            size_t word_pos = std::distance(content.begin(), iter);
            size_t start_pos = word_pos > 25 ? word_pos - 25 : 0;
            size_t end_pos = content.size() > word_pos + 35 ? word_pos + 35 : content.size();
            if (start_pos > end_pos)
            {
                boost_searcher::searcher_log(WARNING,"failed to cut desc form content!");
                return std::move(std::string("null"));
            }
            std::string ret_str(std::move(content.substr(start_pos, end_pos - start_pos)));
            ret_str.append("...");
            return std::move(ret_str);
        }

    private:
        bs_index::index_t *_index;
    };
}
